package com.fandou.base.ui.fragment;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;

import com.fandou.base.BR;
import com.fandou.base.BaseAPP;
import com.fandou.base.R;
import com.fandou.base.ui.BaseUI;
import com.fandou.base.ui.DataBindingConfig;
import com.fandou.base.ui.activity.BaseActivity;
import com.fandou.base.ui.callback.SharedViewModel;
import com.fandou.base.view.LoadingView;

import org.jetbrains.annotations.NotNull;


/**
 * Created by yzj on 2018/12/1.
 */

public abstract class BaseFragment extends Fragment implements BaseUI {

    protected Context mContext;
    protected OnFragmentListener onFragmentListener;

    private static final Handler HANDLER = new Handler();
    protected AppCompatActivity mActivity;
    protected boolean mAnimationLoaded;
    private SharedViewModel mSharedViewModel;
    private ViewModelProvider mFragmentProvider;
    private ViewModelProvider mActivityProvider;
    private ViewModelProvider.Factory mFactory;
    private ViewDataBinding mBinding;
    private TextView mTvStrictModeTip;


    @Override
    public void onAttach(@NotNull Context context) {
        super.onAttach(context);
		mContext = (Activity) context;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initViewModel();

//        mLoadingView = new LoadingView(getActivity());
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        DataBindingConfig dataBindingConfig = getDataBindingConfig();

        //TODO tip: DataBinding 严格模式：
        // 将 DataBinding 实例限制于 base 页面中，默认不向子类暴露，
        // 通过这样的方式，来彻底解决 视图调用的一致性问题，
        // 如此，视图刷新的安全性将和基于函数式编程的 Jetpack Compose 持平。

        // 如果这样说还不理解的话，详见 https://xiaozhuanlan.com/topic/9816742350 和 https://xiaozhuanlan.com/topic/2356748910

        ViewDataBinding binding = DataBindingUtil.inflate(inflater, dataBindingConfig.getLayout(), container, false);
        binding.setLifecycleOwner(this);
        binding.setVariable(BR.vm, dataBindingConfig.getStateViewModel());
        SparseArray bindingParams = dataBindingConfig.getBindingParams();
        for (int i = 0, length = bindingParams.size(); i < length; i++) {
            binding.setVariable(bindingParams.keyAt(i), bindingParams.valueAt(i));
        }
        mBinding = binding;
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

    }


    @Nullable
    @Override
    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
        //TODO 错开动画转场与 UI 刷新的时机，避免掉帧卡顿的现象
        HANDLER.postDelayed(() -> {
            if (!mAnimationLoaded) {
                mAnimationLoaded = true;
                loadInitData();
            }
        }, 280);
        return super.onCreateAnimation(transit, enter, nextAnim);
    }

    protected void loadInitData() {

    }


	public boolean isDebug() {
		return mActivity.getApplicationContext().getApplicationInfo() != null &&
				(mActivity.getApplicationContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
	}

    protected abstract DataBindingConfig getDataBindingConfig();


    /**
     * TODO tip: 警惕使用。非必要情况下，尽可能不在子类中拿到 binding 实例乃至获取 view 实例。使用即埋下隐患。
     * 目前的方案是在 debug 模式下，对获取实例的情况给予提示。
     * <p>
     * 如果这样说还不理解的话，详见 https://xiaozhuanlan.com/topic/9816742350 和 https://xiaozhuanlan.com/topic/2356748910
     *
     * @return
     */
    protected ViewDataBinding getBinding() {
        if (isDebug() && mBinding != null) {
            if (mTvStrictModeTip == null) {
                mTvStrictModeTip = new TextView(getContext());
                mTvStrictModeTip.setAlpha(0.5f);
                mTvStrictModeTip.setTextSize(16);
                mTvStrictModeTip.setBackgroundColor(Color.WHITE);
                mTvStrictModeTip.setText(R.string.debug_fragment_databinding_warning);
                ((ViewGroup) mBinding.getRoot()).addView(mTvStrictModeTip);
            }
        }
        return mBinding;
    }

    protected abstract void initViewModel();

    @Override
    public void onDetach() {
        super.onDetach();
		mContext = null;
    }

    @Override
    public void loading() {
        if (mContext instanceof BaseActivity){
            ((BaseActivity) mContext).loading();
        }
    }

    @Override
    public void loading(String text) {
        if (mContext instanceof BaseActivity){
            ((BaseActivity) mContext).loading(text);
        }
    }

    @Override
    public void endLoading() {
        if (mContext instanceof BaseActivity){
            ((BaseActivity) mContext).endLoading();
        }
    }

    protected void finish() {
        if (onFragmentListener != null) {
            onFragmentListener.onFinish(this);
        }
    }

    //Toast //////////////////////////////////////////////////////////////

    protected void showErrorToast(String message) {
        showToast(R.mipmap.toast_icon_error, message);
    }

    protected void showWarnToast(String message) {
        showToast(R.mipmap.toast_icon_warning, message);
    }

    protected void showInfoToast(String message) {
        showToast(R.mipmap.toast_icon_right, message);
    }

    private void showToast(@DrawableRes int iconRes, String message) {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.toast_layout, null, false);
        ImageView iv_icon = view.findViewById(R.id.toast_icon);
        TextView tv_message = view.findViewById(R.id.toast_message);

        iv_icon.setImageResource(iconRes);
        tv_message.setText(message);

        Toast toast = new Toast(getContext().getApplicationContext());
        toast.setView(view);
        toast.setGravity(Gravity.CENTER, 0, 0);
        toast.setDuration(Toast.LENGTH_SHORT);
        toast.show();
    }


	protected <T extends ViewModel> T getFragmentViewModel(@NonNull Class<T> modelClass) {
		if (mFragmentProvider == null) {
			mFragmentProvider = new ViewModelProvider(this);
		}
		return mFragmentProvider.get(modelClass);
	}

	protected <T extends ViewModel> T getActivityViewModel(@NonNull Class<T> modelClass) {
		if (mActivityProvider == null) {
			mActivityProvider = new ViewModelProvider(mActivity);
		}
		return mActivityProvider.get(modelClass);
	}

	protected ViewModelProvider getAppViewModelProvider(Activity activity) {
		return new ViewModelProvider((BaseAPP) activity.getApplicationContext(),
				getAppFactory(activity));
	}

	private ViewModelProvider.Factory getAppFactory(Activity activity) {
		checkActivity(this);
		Application application = checkApplication(activity);
		if (mFactory == null) {
			mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
		}
		return mFactory;
	}

	private Application checkApplication(Activity activity) {
		Application application = activity.getApplication();
		if (application == null) {
			throw new IllegalStateException("Your activity/fragment is not yet attached to "
					+ "Application. You can't request ViewModel before onCreate call.");
		}
		return application;
	}

	private void checkActivity(Fragment fragment) {
		Activity activity = fragment.getActivity();
		if (activity == null) {
			throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
		}
	}

    protected NavController nav() {
        return NavHostFragment.findNavController(this);
    }

    public SharedViewModel getSharedViewModel() {
        return mSharedViewModel;
    }

    //Fragment //////////////////////////////////////////////////////////////

    protected void startFragment(int containerViewId, Fragment fragment) {
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.add(containerViewId, fragment, Integer.toString(fragment.hashCode()));
        fragmentTransaction.commitAllowingStateLoss();
    }

    protected void closeFragment(Fragment fragment) {
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.remove(fragment);
        fragmentTransaction.commitAllowingStateLoss();
    }



    //Listener //////////////////////////////////////////////////////////////

    public interface OnFragmentListener {
        void onFinish(Fragment fragment);
    }

    public void setOnFragmentListener(OnFragmentListener onFragmentListener) {
        this.onFragmentListener = onFragmentListener;
    }
}
